前面提到了,JS 是實作原型基礎物件導向程式設計(Prototype-based OOP)的語言,那麼這個最重要的原型(Prototype)究竟是什麼?
在 JS 裡面,有幾種取得原型的方法:
Object.getPrototypeOf(obj)
__proto__
(由於效能等原因已被棄用,目前建議僅用於偵錯時檢視物件原型)prototype
屬性,如 String.prototype
以下為使用範例,檢視物件與陣列的原型:
const obj = {};
const arr = [];
console.log(
Object.prototype === Object.getPrototypeOf(obj), // true
Object.prototype === obj.__proto__, // true
Object.getPrototypeOf(obj) === obj.constructor.prototype, // true
Array.prototype === Object.getPrototypeOf(arr), // true
Array.prototype === arr.__proto__, // true
Object.getPrototypeOf(arr) === arr.constructor.prototype, // true
)
從以上程式碼可以看到,obj
和 arr
等實例所找到的原型,和建構子函式 Object
與 Array
連結到的 prototype
是同樣的東西。
知道如何取得原型後,就可以開始來檢視原型鍊了,我們來看看一般物件:
let x = {}
console.log(x.__proto__.toString()) // [object Object]
console.log(x.__proto__.__proto__) // null
console.log(Object.getPrototypeOf(x).toString()) // [object Object]
console.log(Object.getPrototypeOf(Object.getPrototypeOf(x))) // null
console.log(Object.prototype.toString()) // [object Object]
console.log(Object.prototype.__proto__) // null,因為根物件沒有 prototype 這個屬性,只能調用 __proto__
在上面可以看到,不管用哪種方法,一個使用字面值定義的一般物件,原型鍊連結的都是 Object
這個內建物件的 prototype
,這個連結到的物件又稱為「根物件(Root Object)」,從根物件再往上追溯則是 null
,是整個原型鍊的終點。
同樣檢查自訂建構子函式的原型鍊:
function Circle(radius) {
this.radius = radius;
this.draw = function () {
console.log("draw");
}
}
const c = new Circle(1);
console.log(
c.__proto__ === Circle.prototype, // true
c.__proto__.__proto__ === Object.prototype, // true
c.__proto__.__proto__.__proto__ === null, // true
)
被實例化出來的物件 c
,原型鍊找到了 Circle
的 prototype
,再往上同樣是 Object.prototype
,除了少數特例狀況,大多數物件都是直接或間接繼承自這個根物件,最後才遇到了 null
,抵達原型鍊終點。
這種上下如同樹系的連結關係,就是 JS 中的原型與原型鍊原理。
除了取得物件原型外,JS 還擁有 isPrototypeOf
方法,能夠檢視一個物件是否位於一個物件的原型鍊上。
function ObjA() {}
function ObjB() {}
function ObjC() {}
ObjB.prototype = Object.create(ObjA.prototype);
ObjC.prototype = Object.create(ObjB.prototype);
var objC = new ObjC();
console.log(ObjC.prototype.isPrototypeOf(objC)); // true
console.log(ObjB.prototype.isPrototypeOf(objC)); // true
console.log(ObjA.prototype.isPrototypeOf(objC)); // true
console.log(Object.prototype.isPrototypeOf(objC)); // true